package com.d3works.apps.base.utils;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

/**
 * 一些集合处理方法
 *
 * @author wang
 */
public final class Lambda {

  public static final Func1<?, Boolean> TRUE = x -> true;

  public static final Func1<?, Boolean> FALSE = x -> false;

  /**
   * 通过原集合创建新的集合
   *
   * @param listOfIn
   * @param condition
   * @param funGetResult
   * @return
   */
  public static <TIn extends Object, TOut> List<TOut> select(
      Iterable<TIn> listOfIn, Func1<TIn, Boolean> where, Func1<TIn, TOut> convert) {
    ArrayList<TOut> newList = new ArrayList<TOut>();
    if (listOfIn == null) {
      return newList;
    }
    for (TIn item : listOfIn) {
      if (where == null || !where.call(item)) {
        continue;
      }
      TOut r = convert.call(item);
      if (r != null) {
        newList.add(r);
      }
    }
    return newList;
  }

  /**
   * 数组查找
   *
   * @param <T>
   * @param array
   * @param where
   * @return
   */
  public static <T> List<T> find(T[] array, Func1<T, Boolean> where) {
    return find(Arrays.asList(array), where);
  }

  /**
   * 集合查找
   *
   * @param listOfIn
   * @param condition
   * @param funGetResult
   * @return
   */
  public static <T> List<T> find(Iterable<T> listOfIn, Func1<T, Boolean> where) {
    if (listOfIn == null) {
      return null;
    }
    ArrayList<T> newList = new ArrayList<T>();
    for (T item : listOfIn) {
      if (where == null || !where.call(item)) {
        continue;
      }
      newList.add(item);
    }
    return newList;
  }

  /**
   * 通过原集合创建新的集合
   *
   * @param listOfIn
   * @param condition
   * @param funGetResult
   * @return
   */
  public static <TIn extends Object> List<TIn> select(
      Iterable<TIn> listOfIn, Func1<TIn, Boolean> where) {
    return select(listOfIn, where, x -> x);
  }

  /**
   * 通过原集合创建新的集合
   *
   * @param listOfIn
   * @param condition
   * @param funGetResult
   * @return
   */
  public static <TIn extends Object> List<TIn> select(TIn[] listOfIn, Func1<TIn, Boolean> where) {
    return select(listOfIn, where, x -> x);
  }

  /**
   * 查询并集合转换
   *
   * @param listOfIn
   * @param funGetResult
   * @return
   */
  @SuppressWarnings("unchecked")
  public static <TIn extends Object, TOut> List<TOut> convert(
      Iterable<TIn> listOfIn, Func1<TIn, TOut> convert) {
    return select(listOfIn, (Func1<TIn, Boolean>) TRUE, convert);
  }

  /**
   * 查询并集合转换
   *
   * @param listOfIn
   * @param funGetResult
   * @return
   */
  @SuppressWarnings("unchecked")
  public static <TIn extends Object, TOut> List<TOut> convert(
      TIn[] input, Func1<TIn, TOut> convert) {
    return select(asList(input), (Func1<TIn, Boolean>) TRUE, convert);
  }

  /**
   * 通过原集合创建新的集合
   *
   * @param listOfIn
   * @param condition
   * @param funGetResult
   * @return
   */
  public static <TIn extends Object, TOut> List<TOut> select(
      TIn[] listOfIn, Func1<TIn, Boolean> condition, Func1<TIn, TOut> funGetResult) {
    ArrayList<TOut> newList = new ArrayList<TOut>();
    for (TIn item : listOfIn) {
      if (condition.call(item)) {
        newList.add(funGetResult.call(item));
      }
    }
    return newList;
  }

  /**
   * 集合转为Map对象
   *
   * @param iterable
   * @param keyGen
   * @return
   */
  public static <T extends Object> Map<String, T> toMap(T[] iterable, Func1<T, String> keyGen) {
    Map<String, T> map = new HashMap<>();
    for (T obj : iterable) {
      String key = keyGen.call(obj);
      if (map.containsKey(key)) {
        throw new RuntimeException("提供的Key值，无法确写唯一的数据对象");
      }
      map.put(keyGen.call(obj), obj);
    }
    return map;
  }

  public static <T, TK, TV> Map<TK, TV> map(
      Iterable<T> iterable, Func1<T, TK> key, Func1<T, TV> value) {
    return map(iterable, null, key, value);
  }

  /**
   * 转成Map对象
   *
   * @param iterable 输入的集合
   * @param key Key的取值方式
   * @param value Value的取值方式
   * @return
   */
  public static <T, TK, TV> Map<TK, TV> map(
      Iterable<T> iterable, Func1<T, Boolean> condition, Func1<T, TK> key, Func1<T, TV> value) {
    Map<TK, TV> map = new LinkedHashMap<>();
    if (iterable != null) {
      for (T obj : iterable) {
        TK objKey = null;
        if (condition != null) {
          if (condition.call(obj) && (objKey = key.call(obj)) != null) {
            map.put(objKey, value.call(obj));
          }
        } else if ((objKey = key.call(obj)) != null) {
          map.put(objKey, value.call(obj));
        }
      }
    }
    return map;
  }

  public static <T extends Object> Map<String, T> map(
      Iterable<T> iterable, Func1<T, String> keyGen) {
    return map(iterable, keyGen, v -> v);
  }

  /**
   * 创建值字典
   *
   * @param input
   * @param keyFun
   * @param valueFunc
   * @return
   */
  public static <TInput extends Object, TKey extends Object, TValue extends Object>
      Map<TKey, TValue> toMap(
          Iterable<TInput> input, Func1<TInput, TKey> keyFun, Func1<TInput, TValue> valueFunc) {
    Map<TKey, TValue> map = new HashMap<TKey, TValue>();
    for (TInput obj : input) {
      map.put(keyFun.call(obj), valueFunc.call(obj));
    }
    return map;
  }

  /**
   * 按条件分组。
   *
   * @param listOfTValue
   * @param fun
   * @return
   */
  public static <TKey extends Object, TValue extends Object> Map<TKey, List<TValue>> group(
      Iterable<TValue> listOfTValue, Func1<TValue, TKey> fun) {
    HashMap<TKey, List<TValue>> grp = new HashMap<>();
    if (listOfTValue != null) {
      for (TValue item : listOfTValue) {
        TKey key = fun.call(item);
        if (key == null) {
          continue;
        }
        if (grp.containsKey(key)) {
          grp.get(key).add(item);
        } else {
          ArrayList<TValue> ls = new ArrayList<TValue>();
          ls.add(item);
          grp.put(key, ls);
        }
      }
    }
    return grp;
  }

  /**
   * @param coll
   * @param where
   * @param fk
   * @param fv
   * @return
   */
  public static <T, K, V> Map<K, List<V>> group(
      Iterable<T> coll, Func1<T, Boolean> where, Func1<T, K> fk, Func1<T, V> fv) {
    LinkedHashMap<K, List<V>> grp = new LinkedHashMap<>();
    for (T item : coll) {
      K key = null;
      if (where == null) {
        key = fk.call(item);
      } else if (where.call(item)) {
        key = fk.call(item);
      }
      if (key != null) {
        V v = fv.call(item);
        if (grp.containsKey(key)) {
          grp.get(key).add(fv.call(item));
        } else {
          ArrayList<V> ls = new ArrayList<>();
          ls.add(v);
          grp.put(key, ls);
        }
      }
    }
    return grp;
  }

  /**
   * @param listOfTValue
   * @param where
   * @param fun
   * @return
   */
  public static <TKey extends Object, TValue extends Object> Map<TKey, List<TValue>> group(
      Iterable<TValue> listOfTValue, Func1<TValue, Boolean> where, Func1<TValue, TKey> fun) {
    HashMap<TKey, List<TValue>> grp = new HashMap<>();
    for (TValue item : listOfTValue) {
      if (!where.call(item)) {
        continue;
      }
      TKey key = fun.call(item);
      if (grp.containsKey(key)) {
        grp.get(key).add(item);
      } else {
        ArrayList<TValue> ls = new ArrayList<TValue>();
        ls.add(item);
        grp.put(key, ls);
      }
    }
    return grp;
  }

  /**
   * 按条件分组。
   *
   * @param listOfTValue
   * @param fun
   * @return
   */
  public static <TKey extends Object, TValue extends Object> Map<TKey, Set<TValue>> groupSet(
      Iterable<TValue> listOfTValue, Func1<TValue, TKey> fun) {
    HashMap<TKey, Set<TValue>> grp = new HashMap<>();
    for (TValue item : listOfTValue) {
      TKey key = fun.call(item);
      if (grp.containsKey(key)) {
        grp.get(key).add(item);
      } else {
        LinkedHashSet<TValue> ls = new LinkedHashSet<TValue>();
        ls.add(item);
        grp.put(key, ls);
      }
    }
    return grp;
  }

  /**
   * 用于主子表关系的多个单据的合并
   *
   * @param classOfT
   * @param funGetValue
   * @return
   */
  public static <T1, T2> List<T2> combine(
      Iterable<T1> lsOfT1, Func1<T1, Iterable<T2>> funGetValue) {
    ArrayList<T2> ls = new ArrayList<T2>();
    for (T1 itemOfT1 : lsOfT1) {
      if (itemOfT1 == null) {
        continue;
      }
      Iterable<T2> subList = funGetValue.call(itemOfT1);
      if (subList != null) {
        for (T2 item : subList) {
          ls.add(item);
        }
      }
    }
    return ls;
  }

  /**
   * 转成List对象
   *
   * @param objs
   * @return
   */
  public static <T extends Object> List<T> ls(@SuppressWarnings("unchecked") T... objs) {
    ArrayList<T> ls = new ArrayList<T>();
    for (T obj : objs) {
      ls.add(obj);
    }
    return ls;
  }

  public static <T extends Object> List<T> ls(Iterable<T> iterable) {
    return asList(iterable);
  }

  /**
   * 转成List对象
   *
   * @param iterable
   * @return
   */
  public static <T extends Object> List<T> asList(Iterable<T> iterable) {
    ArrayList<T> ls = new ArrayList<T>();
    for (T obj : iterable) {
      ls.add(obj);
    }
    return ls;
  }

  /**
   * 将数据变成可迭代对象
   *
   * <p>判断数组是否为空，过滤数组是为空的元素。
   *
   * @param iterable
   * @return
   */
  public static <T extends Object> Iterable<T> toIterable(
      @SuppressWarnings("unchecked") T... array) {
    ArrayList<T> ls = new ArrayList<T>();
    if (array != null) {
      for (T obj : array) {
        if (obj == null) {
          continue;
        }
        ls.add(obj);
      }
    }
    return ls;
  }

  /**
   * 数组转集合
   *
   * @param array
   * @return
   */
  @SafeVarargs
  public static <T extends Object> Collection<T> asCollection(T... array) {
    return asList(array);
  }

  @SafeVarargs
  public static <T extends Object> List<T> asList(T... array) {
    ArrayList<T> ls = new ArrayList<T>(array.length);
    for (T obj : array) {
      ls.add(obj);
    }
    return ls;
  }

  public static <T> Iterable<T> iterable(T[] array) {
    return (array == null ? new ArrayList<T>() : asList(array));
  }

  public static <T> Iterator<T> iterator(T[] array) {
    return (array == null ? new ArrayList<T>() : asList(array)).iterator();
  }

  /**
   * 将大集合拆分成一组小集合
   *
   * @param lsBigCollection
   * @param splitSize
   * @return
   */
  public static <T> List<List<T>> split(List<T> lsBigCollection, int splitSize) {
    int len = lsBigCollection.size();
    List<List<T>> lsArray = new ArrayList<>();
    if (len <= splitSize) {
      lsArray.add(lsBigCollection);
      return lsArray;
    }
    int splitCount = len / splitSize;
    int modSize = len % splitSize;
    if (modSize > 0) {
      splitCount += 1;
    }
    for (int i = 0; i < splitCount; i++) {
      List<T> lsSmall = new ArrayList<>();
      int startPos = i * splitSize;
      int l = splitSize;
      if (i + 1 == splitCount && modSize > 0) {
        l = modSize;
      }
      for (int j = 0; j < l; j++) {
        lsSmall.add(lsBigCollection.get(startPos + j));
      }
      lsArray.add(lsSmall);
    }
    return lsArray;
  }

  /**
   * 将集合转成数据
   *
   * @param clazz
   * @param coll
   * @return
   */
  @SuppressWarnings("unchecked")
  public static <T> T[] toArray(Class<T> clazz, Collection<T> coll) {
    return toArray(clazz, coll, x -> (T) x);
  }

  /**
   * 将集合转成数据
   *
   * @param clazz
   * @param coll
   * @param fn
   * @return
   */
  @SuppressWarnings("unchecked")
  public static <T> T[] toArray(Class<T> clazz, Collection<?> coll, Func1<Object, T> fn) {
    T[] arr = (T[]) Array.newInstance(clazz, coll.size());
    int i = 0;
    for (Object obj : coll) {
      arr[i++] = fn.call(obj);
    }
    return arr;
  }

  /**
   * forEach 遍历
   *
   * @param iterable
   * @param where
   * @param handler
   */
  public static <T> void forEach(
      Iterable<T> iterable, Func1<T, Boolean> where, Action1<T> handler) {
    if (iterable == null) {
      return;
    }
    for (T item : iterable) {
      if (item == null || !where.call(item)) {
        continue;
      }
      handler.call(item);
    }
  }

  /**
   * forEach 遍历
   *
   * @param iterable
   * @param handler
   */
  public static <T> void forEach(Iterable<T> iterable, Action1<T> handler) {
    forEach(iterable, x -> true, handler);
  }

  /**
   * forEach 遍历
   *
   * @param array
   * @param where
   * @param handler
   */
  public static <T> void forEach(T[] array, Func1<T, Boolean> where, Action1<T> handler) {
    for (T item : array) {
      if (!where.call(item)) {
        continue;
      }
      handler.call(item);
    }
  }

  /**
   * foreach遍历
   *
   * @param array
   * @param handler
   */
  public static <T> void forEach(T[] array, Action1<T> handler) {
    forEach(array, x -> true, handler);
  }

  /**
   * 从数组中找到一个元素
   *
   * @param array
   * @param fn
   * @return
   */
  public static <T> T findOne(T[] array, Func1<T, Boolean> fn) {
    ArrayList<T> ls = new ArrayList<T>();
    for (T obj : array) {
      if (obj == null || !fn.call(obj)) {
        continue;
      }
      ls.add(obj);
    }
    if (ls.size() > 1) {
      throw new RuntimeException(String.format("要求返回1个元素，返回了%s个元素", ls.size()));
    } else if (ls.size() == 0) {
      return null;
    } else {
      return ls.get(0);
    }
  }

  /**
   * @param array
   * @param fn
   * @param convert
   * @return
   */
  public static <T1, T2> T2 findOne(
      Iterable<T1> array, Func1<T1, Boolean> fn, Func1<T1, T2> convert) {
    T1 o = findOne(array, fn);
    return o == null ? null : convert.call(o);
  }

  /**
   * 第一个符合条件的元素
   *
   * @param <T>
   * @param array
   * @param fn
   * @return
   */
  public static <T> T first(Iterable<T> array, Func1<T, Boolean> fn) {
    for (T obj : array) {
      if (obj == null || !fn.call(obj)) {
        continue;
      }
      return obj;
    }
    return null;
  }

  /**
   * 从数组中找到一个元素
   *
   * @param array
   * @param fn
   * @return
   */
  public static <T> T findOne(Iterable<T> array, Func1<T, Boolean> fn) {
    ArrayList<T> ls = new ArrayList<T>();
    for (T obj : array) {
      if (obj == null || !fn.call(obj)) {
        continue;
      }
      ls.add(obj);
    }
    if (ls.size() > 1) {
      throw new RuntimeException(String.format("要求返回1个元素，返回了%s个元素", ls.size()));
    } else if (ls.size() == 0) {
      return null;
    } else {
      return ls.get(0);
    }
  }

  /**
   * 数组排序
   *
   * @param array
   */
  public static <T extends Comparable<T>> void sort(T[] array) {
    int len = 0;
    if (array == null || (len = array.length) <= 1) {
      return;
    }
    len--;
    T a, b;
    for (int i = 0; i < len; i++) {
      int jLen = len - i;
      for (int j = 0; j < jLen; j++) {
        a = array[j];
        b = array[j + 1];
        if (a.compareTo(b) > 0) {
          array[j] = b;
          array[j + 1] = a;
        }
      }
    }
  }

  /**
   * 数据转Set
   *
   * @param items
   * @return
   */
  @SafeVarargs
  public static <T> Set<T> set(T... items) {
    LinkedHashSet<T> set = new LinkedHashSet<>();
    if (items != null && items.length > 0) {
      for (T item : items) {
        if (item == null) {
          continue;
        }
        set.add(item);
      }
    }
    return set;
  }

  /**
   * 带条件的筛查
   *
   * @param items
   * @param where
   * @param fn
   * @return
   */
  public static <T1, T2> Set<T2> set(
      Collection<T1> items, Func1<T1, Boolean> where, Func1<T1, T2> fn) {
    LinkedHashSet<T2> set = new LinkedHashSet<>();
    T2 element;
    for (T1 item : items) {
      if (item == null || !where.call(item) || (element = fn.call(item)) == null) {
        continue;
      }
      set.add(element);
    }
    return set;
  }

  public static <T1, T2> Set<T2> set(Collection<T1> items, Func1<T1, T2> fn) {
    LinkedHashSet<T2> set = new LinkedHashSet<>();
    if (CollectionUtils.isNotEmpty(items)) {
      T2 element;
      for (T1 item : items) {
        if (item == null || (element = fn.call(item)) == null) {
          continue;
        }
        set.add(element);
      }
    }
    return set;
  }

  /**
   * 转Set
   *
   * @param items
   * @return
   */
  public static <T> Set<T> set(Collection<T> items) {
    LinkedHashSet<T> set = new LinkedHashSet<>();
    if (items == null) {
      return set;
    }
    for (T item : items) {
      if (item == null) {
        continue;
      }
      set.add(item);
    }
    return set;
  }

  @SafeVarargs
  public static <T> LinkedHashSet<T> toSortSet(T... items) {
    LinkedHashSet<T> set = new LinkedHashSet<>();
    for (T item : items) {
      if (item == null || StringUtils.isEmpty(item.toString())) {
        throw new RuntimeException("元素不能为空！");
      }
      if (set.contains(item)) {
        throw new RuntimeException("元素重复：" + item.toString());
      }
      set.add(item);
    }
    return set;
  }

  /**
   * 数据集过滤
   *
   * @param listOfT
   * @param condition
   * @return
   */
  public static <T extends Object> List<T> filter(
      Iterable<T> listOfT, Func1<T, Boolean> condition) {
    ArrayList<T> newList = new ArrayList<T>();
    for (T item : listOfT) {
      if (condition.call(item)) {
        newList.add(item);
      }
    }
    return newList;
  }

  /**
   * 对象{@code obj}是否在集合中
   *
   * @param <T>
   * @param obj
   * @param array
   * @return
   */
  public static <T> boolean inCollection(T obj, T[] array) {
    if (obj == null || array == null) {
      return false;
    }
    for (T item : array) {
      if (item != null && item.equals(obj)) {
        return true;
      }
    }
    return false;
  }

  /**
   * 对象{@code obj}是否在集合中
   *
   * @param <T>
   * @param obj
   * @param coll
   * @return
   */
  public static <T> boolean inCollection(T obj, Collection<T> coll) {
    if (obj == null || CollectionUtils.isEmpty(coll)) {
      return false;
    }
    for (T item : coll) {
      if (item != null && item.equals(obj)) {
        return true;
      }
    }
    return false;
  }

  /**
   * 数组判空
   *
   * @param <T>
   * @param array
   * @return
   */
  public static <T> boolean isEmpty(T[] array) {
    return array == null || array.length == 0;
  }

  /**
   * 数组非空
   *
   * @param <T>
   * @param array
   * @return
   */
  public static <T> boolean isNotEmpty(T[] array) {
    return !isEmpty(array);
  }

  /**
   * 集合判空
   *
   * @param <T>
   * @param coll
   * @return
   */
  public static <T> boolean isEmpty(Collection<T> coll) {
    return coll == null || coll.size() == 0;
  }

  /**
   * 集合非空
   *
   * @param <T>
   * @param coll
   * @return
   */
  public static <T> boolean isNotEmpty(Collection<T> coll) {
    return !isEmpty(coll);
  }

  /**
   * 迪卡尔积
   *
   * @param <T>
   * @param coll
   * @return
   */
  public static <T> Collection<Collection<T>> descartes(Collection<Collection<T>> coll) {
    @SuppressWarnings("unchecked")
    Collection<T>[] arr = new Collection[coll.size()];
    int i = 0;
    for (Collection<T> item : coll) {
      arr[i++] = item;
    }
    return descartes(arr);
  }

  /**
   * 迪卡尔积
   *
   * @param <T>
   * @return
   */
  @SafeVarargs
  public static <T> Collection<Collection<T>> descartes(Collection<T>... items) {
    if (items.length == 0 || Lambda.find(Arrays.asList(items), x -> x.size() == 0).size() > 0) {
      return new ArrayList<>(0);
    } else if (items.length == 1) {
      Collection<T> item = items[0];
      Collection<Collection<T>> ls = new ArrayList<>(item.size());
      for (T t : item) {
        ArrayList<T> lsItem = new ArrayList<>(1);
        lsItem.add(t);
        ls.add(lsItem);
      }
      return ls;
    } else if (items.length == 2) {
      return descartes2(items[0], items[1]);
    } else {
      Collection<Collection<T>> n = descartes2(items[0], items[1]);
      for (int i = 2; i < items.length; i++) {
        n = descartes3(n, items[i]);
      }
      return n;
    }
  }

  /**
   * 两个集合的迪卡尔积
   *
   * @param <T>
   * @param c1
   * @param c2
   * @return
   */
  private static <T> Collection<Collection<T>> descartes2(Collection<T> c1, Collection<T> c2) {
    Collection<Collection<T>> lsR = new LinkedList<Collection<T>>();
    for (T a : c1) {
      for (T b : c2) {
        Collection<T> n = new LinkedList<>();
        n.add(a);
        n.add(b);
        lsR.add(n);
      }
    }
    return lsR;
  }

  /**
   * 两个集合的迪卡尔积
   *
   * @param <T>
   * @param a
   * @param b
   * @return
   */
  public static <T> Collection<Collection<T>> descartes3(
      Collection<Collection<T>> c1, Collection<T> c2) {
    Collection<Collection<T>> lsR = new LinkedList<Collection<T>>();
    for (Collection<T> a : c1) {
      for (T b : c2) {
        Collection<T> n = new LinkedList<>(a);
        n.add(b);
        lsR.add(n);
      }
    }
    return lsR;
  }

  /**
   * 获取并删除
   *
   * @param <T>
   * @param coll
   * @param where
   * @return
   */
  public static <T> List<T> selectAndRemove(Collection<T> coll, Func1<T, Boolean> where) {
    List<T> ls = select(coll, where);
    for (T item : ls) {
      coll.remove(item);
    }
    return ls;
  }

  /**
   * 删除符合条件的数据
   *
   * @param <T>
   * @param coll
   * @param where
   * @return
   */
  public static <T> int remove(Collection<T> coll, Func1<T, Boolean> where) {
    List<T> ls = select(coll, where);
    for (T item : ls) {
      coll.remove(item);
    }
    return ls.size();
  }
}
